<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:og="http://ogp.me/ns#" xmlns:fb="http://www.facebook.com/2008/fbml">

<head>
    <meta charset="UTF-8">
    <title>MakeCode Maker - Board Designer</title>
    
<!-- include head.html -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">

<link rel="stylesheet" data-rtl="/blb/rtlsemantic.css" href="/doccdn/semantic.css" />
<link id="blocklycss"  data-rtl="/blb/rtlblockly.css" rel="stylesheet" href="/doccdn/blockly.css" />
<link rel="stylesheet" href="/docfiles/style.css" />
<link rel="stylesheet" href="/docfiles/vs.css" />
<link rel="stylesheet" href="/docfiles/target.css" />

<script src="/doccdn/jquery.js"></script>

<script src="/--embed" type="text/javascript"></script>
<script src="/docfiles/docs.js" type="text/javascript"></script>
<script src="/docfiles/target.js" type="text/javascript"></script>

<style>
    
.ui.accent { color: #ecf0f1; }
.ui.inverted.accent { background: #ecf0f1; }

</style>

<!-- end include head.html -->

    <style>
        #svg, #svg svg {
            width:100%;
            height:100%;
        }
        #svg svg circle:hover,
        #svg svg rect:hover {
            stroke: grey !important;
        }
        #svg svg .selected {
            stroke: yellow !important;
        }
        textarea {
            width:100%;
            height:18rem;
            font-family: monospace;
            font-size: small;
        }
    </style>
    <script type="text/javascript" src="/blb/jquery.js"></script>
    <script>
        const cfgs = {};
        `
// these define keys for getConfig() function
#define CFG_PIN_ACCELEROMETER_INT 1
#define CFG_PIN_ACCELEROMETER_SCL 2
#define CFG_PIN_ACCELEROMETER_SDA 3
#define CFG_PIN_BTN_A 4
#define CFG_PIN_BTN_B 5
#define CFG_PIN_BTN_SLIDE 6
#define CFG_PIN_DOTSTAR_CLOCK 7
#define CFG_PIN_DOTSTAR_DATA 8
#define CFG_PIN_FLASH_CS 9
#define CFG_PIN_FLASH_MISO 10
#define CFG_PIN_FLASH_MOSI 11
#define CFG_PIN_FLASH_SCK 12
#define CFG_PIN_LED 13
#define CFG_PIN_LIGHT 14
#define CFG_PIN_MICROPHONE 15
#define CFG_PIN_MIC_CLOCK 16
#define CFG_PIN_MIC_DATA 17
#define CFG_PIN_MISO 18
#define CFG_PIN_MOSI 19
#define CFG_PIN_NEOPIXEL 20
#define CFG_PIN_RX 21
#define CFG_PIN_RXLED 22
#define CFG_PIN_SCK 23
#define CFG_PIN_SCL 24
#define CFG_PIN_SDA 25
#define CFG_PIN_SPEAKER_AMP 26
#define CFG_PIN_TEMPERATURE 27
#define CFG_PIN_TX 28
#define CFG_PIN_TXLED 29
#define CFG_PIN_IR_OUT 30
#define CFG_PIN_IR_IN 31
#define CFG_PIN_DISPLAY_SCK 32
#define CFG_PIN_DISPLAY_MISO 33
#define CFG_PIN_DISPLAY_MOSI 34
#define CFG_PIN_DISPLAY_CS 35
#define CFG_PIN_DISPLAY_DC 36
#define CFG_DISPLAY_WIDTH 37
#define CFG_DISPLAY_HEIGHT 38
#define CFG_DISPLAY_CFG0 39
#define CFG_DISPLAY_CFG1 40
#define CFG_DISPLAY_CFG2 41
#define CFG_DISPLAY_CFG3 42
#define CFG_PIN_DISPLAY_RST 43
#define CFG_PIN_DISPLAY_BL 44
#define CFG_PIN_SERVO_1 45
#define CFG_PIN_SERVO_2 46
#define CFG_PIN_BTN_LEFT 47
#define CFG_PIN_BTN_RIGHT 48
#define CFG_PIN_BTN_UP 49
#define CFG_PIN_BTN_DOWN 50
#define CFG_PIN_BTN_MENU 51
#define CFG_PIN_LED_R 52
#define CFG_PIN_LED_G 53
#define CFG_PIN_LED_B 54
#define CFG_PIN_LED1 55
#define CFG_PIN_LED2 56
#define CFG_PIN_LED3 57
#define CFG_PIN_LED4 58
#define CFG_SPEAKER_VOLUME 59

#define CFG_PIN_A0 100
#define CFG_PIN_A1 101
#define CFG_PIN_A2 102
#define CFG_PIN_A3 103
#define CFG_PIN_A4 104
#define CFG_PIN_A5 105
#define CFG_PIN_A6 106
#define CFG_PIN_A7 107
#define CFG_PIN_A8 108
#define CFG_PIN_A9 109
#define CFG_PIN_A10 110
#define CFG_PIN_A11 111
#define CFG_PIN_A12 112
#define CFG_PIN_A13 113
#define CFG_PIN_A14 114
#define CFG_PIN_A15 115

#define CFG_PIN_D0 150
#define CFG_PIN_D1 151
#define CFG_PIN_D2 152
#define CFG_PIN_D3 153
#define CFG_PIN_D4 154
#define CFG_PIN_D5 155
#define CFG_PIN_D6 156
#define CFG_PIN_D7 157
#define CFG_PIN_D8 158
#define CFG_PIN_D9 159
#define CFG_PIN_D10 160
#define CFG_PIN_D11 161
#define CFG_PIN_D12 162
#define CFG_PIN_D13 163
#define CFG_PIN_D14 164
#define CFG_PIN_D15 165

#define CFG_NUM_NEOPIXELS 200
#define CFG_NUM_DOTSTARS 201
#define CFG_DEFAULT_BUTTON_MODE 202
#define CFG_SWD_ENABLED 203
#define CFG_FLASH_BYTES 204
`.replace(/#define CFG_(\w+) /g, function (m, id) { cfgs[id] = id });
        console.log(`known cfgs:`)
        console.log(cfgs);

        let boardjson = {}

        function log(text) {
            $('#log').append(text + '\r\n');
        }

        function getSvg() {
            return document.getElementById("svg").querySelector("svg");
        }

        function convertCoords(elt) {
            const svg = getSvg();
            const vb = svg.viewBox.baseVal;
            const offset = svg.getBoundingClientRect();
            const eoffset = elt.getBoundingClientRect();
            const r = {
                x: eoffset.x - offset.left,
                y: eoffset.y - offset.top,
                w: eoffset.width,
                h: eoffset.height
            }
            // rescale for viewbox
            r.x *= vb.width / offset.width;
            r.y *= vb.height / offset.height;
            r.w *= vb.width / offset.width;
            r.h *= vb.height / offset.height;
            return r;
        }

        function addElement(elt) {
            const id = [
                elt.getAttribute("connectorname"),
                elt.getAttribute("inkscape:label"),
                elt.getAttribute("id")]
                .filter(n => !!n && !/^#/.test(n))[0];
            if (!id || boardjson.gpioPinMap[id]) return;
            boardjson.gpioPinMap[id] = id;

            // split subpins RX_TX_A0
            id.split('_').forEach(iid => boardjson.gpioPinMap[iid] = id);

            const rect = convertCoords(elt);

            // handle leds
            let visuals = false;
            if (/(RX|TX)?LED/.test(id)) {
                boardjson.visual.leds.push({
                    x: rect.x,
                    y: rect.y,
                    w: rect.w,
                    h: rect.h,
                    color: '#ff0000',
                    label: id
                })
                visuals = true;
            }
            else if (/^NEOPIXEL/.test(id)) {
                boardjson.visual.leds.push({
                    x: rect.x,
                    y: rect.y,
                    w: rect.w,
                    h: rect.h,
                    color: 'neopixel',
                    label: id
                })
                visuals = true;
                if (id.toLowerCase() == "neopixel" && boardjson.onboardComponents.indexOf("pixel") < 0)
                    boardjson.onboardComponents.push("pixel");
                else if (id.toLowerCase() != "neopixel" && boardjson.onboardComponents.indexOf("neopixel") < 0)
                    boardjson.onboardComponents.push("neopixel");
            }
            else if (/RST|RESET/.test(id) && !boardjson.visual.reset) {
                boardjson.visual.reset = {
                    x: rect.x,
                    y: rect.y,
                    w: rect.w,
                    h: rect.h
                }
                visuals = true;
            }
            else if (/GND/.test(id) && boardjson.groundPins.indexOf(id) < 0)
                boardjson.groundPins.push(id);
            else if (/(\+3V3|3V|VCC|VOUT)/.test(id) && boardjson.threeVoltPins.indexOf(id) < 0)
                boardjson.threeVoltPins.push(id);
            else if (/(BUTTON|BTN_)/.test(id)) {
                if (boardjson.onboardComponents.indexOf("buttons") < 0)
                    boardjson.onboardComponents.push("buttons");
            }

            if (!visuals) {
                if (Math.abs(rect.w - 15) > 2 || Math.abs(rect.h - 15) > 2) {
                    log(`pin ${id}: ${rect.x >> 0}, ${rect.y >> 0} - ${rect.w >> 0}x${rect.h >> 0}`)
                }
                boardjson.visual.pinBlocks.push({
                    x: rect.x,
                    y: rect.y,
                    labels: [id]
                });
            }
            elt.classList.add("selected")
            updateJSON();
        }

        function updateSvg() {
            $('#svg').html($('#svginput').val());
            $('#log').text("");
            boardjson = {
                driveDisplayName: "",
                visual: {
                    image: "pkg://board.svg",
                    outlineImage: "pkg://boardwireframe.svg",
                    useCrocClips: false,
                    width: 200,
                    height: 200,
                    pinDist: 15,
                    pinBlocks: [],
                    leds: []
                },
                gpioPinMap: {},
                groundPins: [],
                threeVoltPins: [],
                spiPins: {
                    "MOSI": "MOSI",
                    "MISO": "MISO",
                    "SCK": "SCK"
                },
                i2cPins: {
                    "SDA": "SDA",
                    "SCL": "SCL"
                },
                onboardComponents: [],
                marginWhenBreadboarding: [
                    0,
                    0,
                    80,
                    0
                ]
            }
            const svg = getSvg();
            boardjson.visual.width = svg.viewBox.baseVal.width;
            boardjson.visual.height = svg.viewBox.baseVal.height;
            const elts = svg.querySelectorAll("*")
            for (let i = 0; i < elts.length; ++i) {
                const elt = elts[i];
                const id = elt.getAttribute("connectorname") || elt.getAttribute("id");
                if (/^[!+\.A-Z0-9_]+$/.test(id))
                    addElement(elt);
            }

            // Get point in global SVG space
            const pt = svg.createSVGPoint();
            function cursorPoint(evt) {
                pt.x = evt.clientX;
                pt.y = evt.clientY;
                return pt.matrixTransform(svg.getScreenCTM().inverse());
            }
            svg.addEventListener('mousemove', function (evt) {
                const loc = cursorPoint(evt);
                $('#pos').text(`${loc.x >> 0}, ${loc.y >> 0}`);
            }, false);

            updateJSON();
        }

        function updateJSON() {
            $('#json').val(JSON.stringify(boardjson, null, 2));
            let configts = `namespace config {`;
            Object.keys(boardjson.gpioPinMap)
                .filter(pinid => !/^(RESET|GND\d*|3V3?|5V|VIN|USHIELD)$/.test(pinid))
                .forEach(pinid => {
                let found = false;
                ['', 'PIN_'].forEach(prefix => {
                    const pid = prefix + pinid;
                    if (cfgs[pid]) {
                        found = true;
                        configts += `
    export const ${pid} = DAL.?;`
                        if (pid == "PIN_NEOPIXEL") {
                            configts += `
    export const NUM_NEOPIXEL = 1;`
                        }
                    }
                })
                if (!found)
                    configts += `
    // unknown pin id ${pinid}`;
            })
            configts += `
    // additional pins to look for: SPI, I2C, Flash
}`;
            $(`#configts`).val(configts);
        }

        $(function () {
            $('#svginput').change(updateSvg);
        })
    </script>
</head>

<body id='root' class='root'>
    <div class="ui container">
        <h1>MakeCode Maker Board designer</h1>
        <p>A handy helper to integrate a new board in maker.makecode.com</p>
        <h2>preparing board.svg</h2>
        <ul class="ui small">
            <li>
                grab the "breadboard" svg of Fritzing board image
            </li>
            <li>
                resize your image (in Inkscape or other) so that a header is precisely 15px
            </li>
            <li>
                (optional) remove extra components to simplify image
            </li>
            <li>
                LEDs: set the id of the svg element to LED...
            </li>
            <li>
                Reset button: set the id of the svg to RESET
            </li>
            <li>
                Pins: set the id of the svg for headers to the pin names (should be done already)
            </li>
        </ul>
        <div class="ui segment">
            <h2>board.svg</h2>
            <ul>
                <li>Save this file as <code>board.svg</code> in your project and it to the <code>files</code> list in <code>pxt.json</code></li>
                <li><emph>Keep it small!</emph>: remove extra graphical elements and run <a href="https://github.com/svg/svgo" target="_blank">SVGO</a> to shrink the size.</li>
            </ul>
            <textarea class="ui fluid" id="svginput"></textarea>
        </div>
        <div class="ui segment" id="svg">
        </div>
        <div class="ui smaller" id="pos"></div>
        <pre class="ui smaller" id="log"></pre>
        <div class="ui segment">
            <h2>board.json</h2>
            <textarea class="ui fluid" readonly id="json"></textarea>
        </div>
        <div class="ui segment">
            <h2>config.ts</h2>
            <textarea class="ui fluid" readonly id="configts"></textarea>
        </div>
    </div>
    
<!-- include footer.html -->
<footer class="ui vertical footer segment hideprint" aria-hidden="false">
    <div class="ui center aligned container">
        <div class="ui horizontal link list">
            <a class="item" href="https://twitter.com/intent/follow?screen_name=MsMakeCode" title="Follow us on Twitter" target="_blank"
                rel="noopener">
                <i class="huge icon twitter"></i>
            </a>
            <a class="item" href="https://www.instagram.com/makecode/" title="Follow us on Instagram" target="_blank" rel="noopened">
                <i class="huge icon instagram"></i>
            </a>
            <a class="item" href="https://www.youtube.com/channel/UCye7YlvFUUQ1dSy0WZZ1T_Q" title="Subscribe to our YouTube channel"
                target="_blank" rel="noopened">
                <i class="huge icon youtube"></i>
            </a>
            <a class="item" href="mailto:makecode@microsoft.com" title="Send us an Email" rel="noopened">
                <i class="huge icon mail"></i>
            </a>
        </div>
        <div class="ui section divider"></div>
        <div class="ui container horizontal small divided link list">
            <!-- <a class="item" href="https://makecode.com/contact" target="_blank" rel="noopener">Contact Us</a> -->
            <a class="item" href="https://makecode.com/privacy" target="_blank" rel="noopener">Privacy &amp; Cookies</a>
            <a class="item" href="https://makecode.com/termsofuse" target="_blank" rel="noopener"> Terms Of Use</a>
            <a class="item" href="https://makecode.com/trademarks" target="_blank" rel="noopener">Trademarks</a>
            <div class="item">© 2018 Microsoft</div>
        </div>
        <div class="ui container horizontal small divided link list">
            <a class="ui centered item" href="https://makecode.com/" title="Microsoft MakeCode" target="_blank" rel="noopener">Powered by Microsoft MakeCode</a>
        </div>
    </div>
</footer>

<!-- end include footer.html -->

    
<!-- include tracking.html -->
<script type="text/javascript" src="/doccdn/pxtweb.js"></script>
<script type="text/javascript">
    window.loadAppInsights = function (includeCookie) {
        var appInsights=window.appInsights||function(config){
            function i(config){t[config]=function(){var i=arguments;t.queue.push(function(){t[config].apply(t,i)})}}var t={config:config},u=document,e=window,o="script",s="AuthenticatedUserContext",h="start",c="stop",l="Track",a=l+"Event",v=l+"Page",y=u.createElement(o),r,f;y.src=config.url||"https://az416426.vo.msecnd.net/scripts/a/ai.0.js";u.getElementsByTagName(o)[0].parentNode.appendChild(y);try{t.cookie=u.cookie}catch(p){}for(t.queue=[],t.version="1.0",r=["Event","Exception","Metric","PageView","Trace","Dependency"];r.length;)i("track"+r.pop());return i("set"+s),i("clear"+s),i(h+a),i(c+a),i(h+v),i(c+v),i("flush"),config.disableExceptionTracking||(r="onerror",i("_"+r),f=e[r],e[r]=function(config,i,u,e,o){var s=f&&f(config,i,u,e,o);return s!==!0&&t["_"+r](config,i,u,e,o),s}),t
        }({
            instrumentationKey:"9801ed01-c40f-46ec-aa40-2a1742a9e71c",
            disableAjaxTracking: true,
            overridePageViewDuration: false,
            disableExceptionTracking: true,
            isCookieUseDisabled: !includeCookie,
            isStorageUseDisabled: !includeCookie,
            url: "/doccdn/ai.0.js"
        });
        window.appInsights=appInsights;
        appInsights.queue.push(function () {
            appInsights.context.addTelemetryInitializer(function (envelope) {
                if (typeof pxtConfig === "undefined") return;
                var telemetryItem = envelope.data.baseData;
                telemetryItem.properties = telemetryItem.properties || {};
                telemetryItem.properties["target"] = pxtConfig.targetId;
                telemetryItem.properties["stage"] = (pxtConfig.relprefix || "/--").replace(/[^a-z]/ig, '')
                telemetryItem.properties["cookie"] = includeCookie;
                if (typeof Windows !== "undefined")
                    telemetryItem.properties["WindowsApp"] = 1;
                var userAgent = navigator.userAgent.toLowerCase();
                var userAgentRegexResult = /\belectron\/(\d+\.\d+\.\d+.*?)(?: |$)/i.exec(userAgent); // Example navigator.userAgent: "Mozilla/5.0 Chrome/61.0.3163.100 Electron/2.0.0 Safari/537.36"
                if (userAgentRegexResult) {
                    telemetryItem.properties["Electron"] = 1;
                    telemetryItem.properties["ElectronVersion"] = userAgentRegexResult[1];
                }
                if (typeof pxtElectron !== "undefined") {
                    telemetryItem.properties["PxtElectron"] = 1;
                    telemetryItem.properties["ElectronVersion"] = pxtElectron.versions.electronVersion;
                    telemetryItem.properties["ChromiumVersion"] = pxtElectron.versions.chromiumVersion;
                    telemetryItem.properties["NodeVersion"] = pxtElectron.versions.nodeVersion;
                    telemetryItem.properties["PxtElectronVersion"] = pxtElectron.versions.pxtElectronVersion;
                    telemetryItem.properties["PxtCoreVersion"] = pxtElectron.versions.pxtCoreVersion;
                    telemetryItem.properties["PxtTargetVersion"] = pxtElectron.versions.pxtTargetVersion;
                    telemetryItem.properties["PxtElectronIsProd"] = pxtElectron.versions.isProd;
                }
            });
        });
        appInsights.trackPageView(null, scrubUrl(window.location.toString()), {urlReferrer: scrubUrl(document.referrer.toString())});

        //Scrub the key (if any) from the URL.
        function scrubUrl(url) {
            var scriptIdRegex = /(?:\d{5}-\d{5}-\d{5}-\d{5})|(?:_[0-9a-zA-Z]{12})/g;
            return url.replace(scriptIdRegex, "xxxxx-xxxxx-xxxxx-xxxxx");
        }
    }
    pxt.initAnalyticsAsync();
</script>
<!-- end include tracking.html -->

</body>

</html>